package org.jdcenter.rmi.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.logging.Logger;

import static org.jdcenter.rmi.impl.Glossary.server;

import org.jdcenter.rmi.JDSCallback;
import org.jdcenter.rmi.abs.*;


public class LocalChannel implements RpcHandler, Channel {
    private static Logger aLogger = Logger.getLogger(LocalChannel.class.getCanonicalName());
    private Logger getLogger() {
        return aLogger;
    }

    private int cookie;
    private JDSCallback callback;
    private RemoteChannel channel;

    public LocalChannel(RemoteChannel channel, JDSCallback callback) throws RemoteException {
        assert callback != null;

        this.callback = callback;
        this.channel = channel;
        try {
            UnicastRemoteObject.exportObject(this);
            this.cookie = this.channel.advise(this, -1);
        } catch(HandlerResumeException e) {
            assert false;
        }
    }

    public JDSCallback getCallback() {
        return callback;
    }

    public void setCallback(JDSCallback callback) {
        this.callback = callback;
    }

    public void close() {
        try {
            this.channel.unadvise(cookie);
            this.channel = null;
            UnicastRemoteObject.unexportObject(this, true);
        } catch (RemoteException e) {
            // Need do nothing
            getLogger().warning(Thrower.toString(e));
        }
    }

    private Object retry_i(String method,Object[] args) throws ChannelException, HandlerException {
        try {
            getLogger().info("Try to re-advise and invoke again ...");
            cookie = channel.advise(this, -1);
            return channel.invoke(cookie,method,args);
        } catch (HandlerResumeException e) {
        	
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        } catch (HandlerInvalidException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        } catch (HandlerRejectedException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        } catch (RemoteException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        }
    }

    private Object retry(String method,Object[] args) throws ChannelException, HandlerException {
        try {
            getLogger().info("Try to resume and invoke again ...");
            cookie = channel.advise(this, cookie);
            return channel.invoke(cookie,method,args);
        } catch (HandlerResumeException e) {
            getLogger().warning(Thrower.toString(e));
            return retry_i(method,args);
        } catch (HandlerInvalidException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        } catch (HandlerRejectedException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        } catch (RemoteException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        }
    }

    public Object invoke(String method,Object[] args,Object result) throws ChannelException, HandlerException {
        try {
        	System.out.println("????????????????//");
            getLogger().finest("Client ==> " + args);
            if(result == null){
            	Object res = channel.invoke(cookie,method,args);
            	 System.out.println("(((((((((((((((//");
            	 return res;
            }else{
            	return callback(result);
            }   
           
        } catch(RemoteException e) {
            getLogger().severe(Thrower.toString(e));
            throw new ChannelException(e);
        } catch (HandlerInvalidException e) {
            getLogger().warning(Thrower.toString(e));
            return retry(method,args);
        } catch (HandlerRejectedException e) {
            getLogger().warning(Thrower.toString(e));
            return retry(method,args);
        }
    }
    public Object callback(Object result) throws RemoteException, HandlerException {
        getLogger().finest(server() + " ==> " + result);
        if(callback == null)
            return InvokeResult.FALSE;
        return callback.callback(result, this);
    }
}

